package org.yunai.swjg.server.core.role;

import org.yunai.swjg.server.core.constants.PlayerConstants;
import org.yunai.swjg.server.module.item.vo.Item;
import org.yunai.swjg.server.module.player.template.LevelTemplate;
import org.yunai.swjg.server.module.player.template.VocationTemplate;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.rpc.message.S_C.S_C_PlayerAddExpResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_PlayerLevelUpResp;
import org.yunai.yfserver.object.KeyValuePair;
import org.yunai.yfserver.util.MathUtils;

import java.util.Arrays;
import java.util.List;

/**
 * 玩家/伙伴角色基本实现
 * User: yunai
 * Date: 13-5-30
 * Time: 上午12:53
 */
public abstract class Role extends AbstractRole {

    protected static class BaseProperties {

        private final int strength;
        private final int stunt;
        private final int magic;
        private final int hp;

        public BaseProperties(int strength, int stunt, int magic, int hp) {
            this.strength = strength;
            this.stunt = stunt;
            this.magic = magic;
            this.hp = hp;
        }
    }

    /**
     * 玩家<br />
     * 该属性目前主要用于调用{@link org.yunai.swjg.server.module.player.vo.Player#message(org.yunai.yfserver.message.IMessage)}
     */
    protected Player player;

    protected Role(RoleDef.Unit unit) {
        super(unit);
    }

    /**
     * 重新计算二级属性，并根据noticeClient判断是否通知客户端<br />
     *
     * @param noticeClient 是否通知客户端
     */
    public void calcRoleProps(boolean noticeClient) {
        long t1 = System.nanoTime();
//        double strength = 0D;
        short level = getLevel();
        // 准备计算的属性
        double strength = 0D;
        double stunt = 0D;
        double magic = 0D;
        double hp = 0D;
        double strengthAttack = 0D;
        double stuntAttack = 0D;
        double magicAttack = 0D;
        double strengthDefense = 0D;
        double stuntDefense = 0D;
        double magicDefense = 0D;
        double crit = 0D;
        double hit = 0D;
        double poJi = 0D;
        double biSha = 0D;
        double toughness = 0D;
        double dodge = 0D;
        double block = 0D;
        double mingLi = 0D; // TODO 这个等猎明出来在搞
        double xianGong = 0D;
        double power = 0D;
        // 基础武力/绝技/法术属性
        BaseProperties baseProperties = getProperties();
        strength += baseProperties.strength;
        stunt += baseProperties.stunt;
        magic += baseProperties.magic;
        // 初始职业(包括等级)
        VocationTemplate vocationTemplate = VocationTemplate.get(getVocation());
        int grow = level - 1;
        hp += grow * vocationTemplate.getGrowHp();
        strengthAttack += grow * vocationTemplate.getGrowStrengthAttack();
        stuntAttack += grow * vocationTemplate.getGrowStuntAttack();
        magicAttack += grow * vocationTemplate.getGrowMagicAttack();
        strengthDefense += vocationTemplate.getBaseStrengthDefense() + grow * vocationTemplate.getGrowStrengthDefense();
        stuntDefense += vocationTemplate.getBaseStuntDefense() + grow * vocationTemplate.getGrowStuntDefense();
        magicDefense += vocationTemplate.getBaseMagicDefense() + grow * vocationTemplate.getGrowMagicDefense();
        crit += vocationTemplate.getBaseCrit();
        hit += vocationTemplate.getBaseHit();
        poJi += vocationTemplate.getBasePoJi();
        biSha += vocationTemplate.getBaseBiSha();
        toughness += vocationTemplate.getBaseToughness();
        dodge += vocationTemplate.getBaseDodge();
        block += vocationTemplate.getBaseBlock();
        xianGong += level * 5;
        // 培养
        strength += getDevelopStrength();
        stunt += getDevelopStunt();
        magic += getDevelopMagic();
        // 丹药
        strength += getMedicineStrengthAddTotal();
        stunt += getMedicineStuntAddTotal();
        magic += getMedicineMagicAddTotal();
        // TODO 命格
        xianGong += mingLi / 40;
        // TODO 装备
//        // 从装备获得的属性 (参考下表格）
        List<Item> equipments = getEquipments();
        for (Item equipment : equipments) {
            System.out.println("TODO 装备属性获得未计算");
        }
        // 武力/绝技/法术换算给攻击/防御、先攻
        // 在10级之内，每点额外三围提供1点对应的攻击属性以及0.7点对应的防御属性，如遇小数，向上取整；
        // 从11级开始，每点额外三围提供角色等级*1点对应的攻击属性以及角色等级*0.7点对应的防御属性，如遇小数，向上取整；
        // XX攻击 = XX攻击 + （XX - 50) * factor
        // XX防御 = XX防御 + （XX - 50) * factor * 0.7
        int attackFactor = MathUtils.ceilX(level, 10);
        double defenseFactor = MathUtils.ceilX(level, 10) * 0.7;
        strengthAttack += (strength - vocationTemplate.getBaseStrength()) * attackFactor;
        stuntAttack += (stunt - vocationTemplate.getBaseStunt()) * attackFactor;
        magicAttack += (magic - vocationTemplate.getBaseMagic()) * attackFactor;
        strengthDefense += (strength - vocationTemplate.getBaseStrength()) * defenseFactor;
        stuntDefense += (stunt - vocationTemplate.getBaseStunt()) * defenseFactor;
        magicDefense += (magic - vocationTemplate.getBaseMagic()) * defenseFactor;
        xianGong += (strength + stunt + magic) / 3; // 个人先攻= 等级*5 + 命力/40 + 装备 + 三围总和/3 ; 队伍总先攻值 = 上阵5人个人先攻总和 + 阵形等级*4000
        // 攻击/防御作用于战力。战力 = (普通攻击 + 法术攻击) * 1 + (绝技攻击) / 3;
        power += strengthAttack + magicAttack + stuntAttack / 3;
        // 赋值回去
        super.setStrength(MathUtils.ceil2Int(strength));
        super.setStunt(MathUtils.ceil2Int(stunt));
        super.setMagic(MathUtils.ceil2Int(magic));
        // TODO 临时测试
//        hpMax = 1000000D;
        super.setHpMax(MathUtils.ceil2Int(hp));
        super.setHpCur(MathUtils.ceil2Int(hp));
        super.setStrengthAttack(MathUtils.ceil2Int(strengthAttack));
        super.setStuntAttack(MathUtils.ceil2Int(stuntAttack));
        super.setMagicAttack(MathUtils.ceil2Int(magicAttack));
        super.setStrengthDefense(MathUtils.ceil2Int(strengthDefense));
        super.setStuntDefense(MathUtils.ceil2Int(stuntDefense));
        super.setMagicDefense(MathUtils.ceil2Int(magicDefense));
        super.setCrit(MathUtils.ceil2Int(crit));
        super.setHit(MathUtils.ceil2Int(hit));
        super.setPoJi(MathUtils.ceil2Int(poJi));
        super.setBiSha(MathUtils.ceil2Int(biSha));
        super.setToughness(MathUtils.ceil2Int(toughness));
        super.setDodge(MathUtils.ceil2Int(dodge));
        super.setBlock(MathUtils.ceil2Int(block));
        super.setXianGong(MathUtils.ceil2Int(xianGong));
        super.setPower(MathUtils.ceil2Int(power));
        super.setMingLi(MathUtils.ceil2Int(mingLi));
        // 通知客户端
        if (noticeClient) {
            noticeChangedProps(true);
        }
//
//        // TODO 临时debug用的
//        Map<String, Object> propMap = new HashMap<>();
//        propMap.put("strength", strength);
//        propMap.put("dexterity", dexterity);
//        propMap.put("intelligence", intelligence);
//        propMap.put("attack", attack);
//        propMap.put("defense", defense);
//        propMap.put("crit", crit);
//        propMap.put("hit", hit);
//        propMap.put("attackSpeed", attackSpeed);
//        propMap.put("hpMax", hpMax);
//        propMap.put("mpMax", mpMax);
//        System.out.println(JSON.toJSONString(propMap));
        // TODO 发消息给竞技场
        // TODO 以后需要调用下队伍！因为队伍先攻
        long t2 = System.nanoTime();
        System.err.println("计算属性所需时间：" + (t2 - t1) + "纳秒");
    }

    /**
     * 发送更新属性给客户端
     *
     * @param reset 是否重置属性更新标识
     */
    public void noticeChangedProps(boolean reset) {
        // 属性变化
        List<KeyValuePair<Integer, Integer>> props = super.primaryProps.getChangedNum(); // 一级属性
        props.addAll(super.secondaryProps.getChangedNum()); // 二级属性
        if (props.size() > 0) {
            player.message(RoleMessageBuilder.build(this, props));
        }
        List<KeyValuePair<Integer, Object>> stringProps = Arrays.asList(super.stringProps.getChanged());
        if (stringProps.size() > 0) {
            player.message(RoleMessageBuilder.build2(this, stringProps));
        }
        // 清理变化标记
        if (reset) {
            super.clearPropsChanged();
        }
    }

    /**
     * 发送所有属性给客户端
     */
    public void noticeProps() {
        List<KeyValuePair<Integer, Integer>> props = super.primaryProps.getAllNum(); // 一级属性
        props.addAll(super.secondaryProps.getAllNum()); // 二级属性
        player.message(RoleMessageBuilder.build(this, props));
        List<KeyValuePair<Integer, Object>> stringProps = Arrays.asList(super.stringProps.getAll());
        player.message(RoleMessageBuilder.build2(this, stringProps));
    }

    /**
     * 增加经验<br />
     * <pre>
     *     1. 增加经验并入库
     *     2. 提示客户端经验增加消息
     *     3. 发送属性变化消息
     *     4. 如果经验满足升级，则进行升级
     * </pre>
     *
     * @param incrExp 增加的经验
     */
    public void addExp(Integer incrExp) {
        if (incrExp <= 0) {
            return;
        }
        // 增加经验/并入库/提示客户端经验增加/发送属性变化消息
        setExp(getExp() + incrExp);
        onModified();
        player.message(new S_C_PlayerAddExpResp(incrExp));
        noticeChangedProps(true);
        // 判断是否够升级
        if (getLevel() < PlayerConstants.LEVEL_MAX) {
            LevelTemplate template = LevelTemplate.get(getLevel());
            if (getExp() >= template.getExp()) {
                levelUp();
            }
        }
    }

    /**
     * 玩家等级升级，支持连续升级<br />
     * <pre>
     *     1. 升级（重新计算剩余经验），并入库
     *     2. 提示客户端玩家等级升级
     *     3. 发送属性变化给客户端
     * </pre>
     */
    private void levelUp() {
        boolean success = false;
        while (getLevel() < PlayerConstants.LEVEL_MAX) {
            LevelTemplate template = LevelTemplate.get(getLevel());
            if (getExp() < template.getExp()) {
                break;
            }
            // 计算经验并标记升级成功
            setExp(getExp() - template.getExp());
            setLevel((short) (getLevel() + 1));
            success = true;
            // 提示客户端升级
            player.message(new S_C_PlayerLevelUpResp(getLevel()));
        }
        // 升级成功后
        if (success) {
            // 重新计算属性并通知客户端属性变化
            calcRoleProps(true);
            // 入库
            onModified();
        }
    }

    public void changeMedicineStrength(int position, int value) {
        super.setMedicineStrength(position, value);
        // 重新计算属性并通知客户端属性变化
        calcRoleProps(true);
        // 入库
        onModified();
    }

    public void changeMedicineStunt(int position, int value) {
        super.setMedicineStunt(position, value);
        // 重新计算属性并通知客户端属性变化
        calcRoleProps(true);
        // 入库
        onModified();
    }

    public void changeMedicineMagic(int position, int value) {
        super.setMedicineMagic(position, value);
        // 重新计算属性并通知客户端属性变化
        calcRoleProps(true);
        // 入库
        onModified();
    }

    protected abstract List<Item> getEquipments();

    protected abstract BaseProperties getProperties();

    protected abstract void onModified();
}
